/*
 * @Author       : 乐乐侠在此
 * @Date         : 2024-02-20 10:24:57
 * @LastEditors  : Please set LastEditors
 * @LastEditTime : 2024-02-22 10:09:18
 * @Description  : 实现一个线程池
 */
#include <iostream>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <deque>
#include <vector>
#include <functional>

using namespace std;

class thread_pool
{
private:
    vector<thread> threads;        // 线程队列
    deque<function<void()>> tasks; // 任务队列
    mutex mtx;                     // 互斥锁 用于控制数据竞态
    condition_variable cv;         // 条件变量，用于同步线程队列和任务队列的工作节拍

    bool stop; // 终止信号
public:
    /* 这个函数用于向任务队列中插入一个任务，并且通知线程队列去处理一条任务 */
    template <class F, class... Args>
    void endeque(F &&f, Args &&...args)
    {
        function<void()> task = bind(forward<F>(f), forward<Args>(args)...);
        unique_lock<mutex> lock(mtx);
        tasks.emplace_back(move(task)); // 插入上面绑定的任务 使用移动语义可以提高效率节省空间
        lock.unlock();
        cv.notify_one(); // 通知线程队列去处理第一个任务
    }

    /*在构造函数中创建若干个线程队列，并且都阻塞在条件变量处 */
    thread_pool(int thread_number) : stop(false)
    {
        for (int i = 0; i < thread_number; i++)
        {
            threads.emplace_back([this]() 
            {
                while(1) {
                    /************  阻塞条件变量  *****************/
                    unique_lock<mutex> lock(mtx);
                    cv.wait(lock,[this](){
                        return !tasks.empty() || stop; //任务队列不空或者已经停止就返回true，否则返回false
                    });

                    /*************  死循环的出口  ****************/
                    if(stop && tasks.empty()) {return;}; //只有线程已经停止并且任务队列已经空了，就是释放掉线程


                    /*************  处理一个任务  ****************/
                    auto task = tasks.front(); //拿到队首任务

                    tasks.pop_front(); //移除已去除的任务
                    lock.unlock();
                    task();
                }; 
            });
        }
    };

    /*在析构函数中将任务队列剩余任务都进行完再结束*/
    ~thread_pool()
    {
        cout<<" * starting Destruct..."<<endl;
        unique_lock<mutex> lock(mtx);
        stop = true;
        lock.unlock();

        cv.notify_all();
        for (auto &t : threads)
        {
            t.join();
        }
        cout<<" * Destruct finished."<<endl;
    };
};

int main()
{
    thread_pool pool(5);

    for (size_t i = 0; i < 50; i++)
    {
        pool.endeque([i]()
        {
            cout<<"this is the no."<<i<<" task"<<endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            cout<<"The no."<<i<<" task is done"<<endl; 
        });
    }

    cout<<"guojiayi1    "<<endl;
    //system("pause");
    pool.~thread_pool();
    system("pause");


}
